﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ThreeByte.ActiveDeck.Data;
using System.Xml.Linq;
using ThreeByte.ActiveDeck.Content;
using ThreeByte.ActiveDeck.Service;
using log4net;

namespace ThreeByte.ActiveDeck.Config
{
    public class PresentationConfiguration
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(PresentationConfiguration));
        
        public Dictionary<Guid, GraphicResource> Resources{
            get;
            private set;
        }

        public Dictionary<Guid, VideoResource> MediaResources {
            get;
            private set;
        }

        private Dictionary<int, List<XElement>> SlideNodes = new Dictionary<int, List<XElement>>();
        private List<int> _orderedSlideIds = new List<int>();
        private CoreConfig BaseConfig;

        public PresentationConfiguration(CoreConfig coreConfig) {
            Resources = new Dictionary<Guid, GraphicResource>();
            MediaResources = new Dictionary<Guid, VideoResource>();

            BaseConfig = coreConfig;
            if (BaseConfig.Logo != null) {
                Resources[BaseConfig.Logo.ID] = BaseConfig.Logo;
            }
            if(BaseConfig.HomePage != null) {
                Resources[BaseConfig.HomePage.ID] = BaseConfig.HomePage;
            }
            if(BaseConfig.EndPage != null) {
                Resources[BaseConfig.EndPage.ID] = BaseConfig.EndPage;
            }
        }


        public void AddPageConfig(int slideId, PresentationPageConfiguration pageConfig) {
            XNamespace ns = ServiceConfiguration.NAMESPACE;

            foreach(Guid g in pageConfig.Resources.Keys) {
                bool uniqueGraphic = true;
                //Test if the resource is equivalent to any existing resource
                if(Resources.ContainsKey(g)) {
                    //The resource itself is already included
                    continue;
                }
                
                foreach(Guid gg in Resources.Keys) {
                    //Check the hash first -- this should be quick
                    if(pageConfig.Resources[g].Hash == Resources[gg].Hash) {
                        if(pageConfig.Resources[g].IsByteEquivalent(Resources[gg])) {
                            log.InfoFormat("Consolidating graphic resources {0} --> {1}", g, gg);
                            pageConfig.ReplaceGraphicResourceID(g, gg);
                            uniqueGraphic = false;
                            break;  //Only need to consolidate once
                        }
                    }
                }
                
                if(uniqueGraphic) {
                    Resources[g] = pageConfig.Resources[g];
                }
            }

            //Add Video Resources
            foreach(Guid g in pageConfig.MediaResources.Keys) {
                MediaResources[g] = pageConfig.MediaResources[g];
            }


            //Add the XML components
            if(!(SlideNodes.ContainsKey(slideId))) {
                SlideNodes[slideId] = new List<XElement>();
                _orderedSlideIds.Add(slideId);
            }
            SlideNodes[slideId].Add(pageConfig.ConfigXml);
        }

        public XElement ToXml() {
            XNamespace ns = ServiceConfiguration.NAMESPACE;
            XElement rootNode = new XElement(ns + "Presentation");
            rootNode.Add(new XAttribute("ID", BaseConfig.ID));
            rootNode.Add(new XAttribute("SlideCount", SlideNodes.Count));

            rootNode.Add(AuthorizationUtil.ToAuthenticationXML(BaseConfig.PresenterLogin));

            if((BaseConfig.KioskConfig != null)
                && (BaseConfig.KioskConfig.Enabled)) {
                    rootNode.Add(BaseConfig.KioskConfig.ToXml());
            }

            rootNode.Add(new XElement(ns + "UserAccess", new XAttribute("Value", DataUtil.UserAccessToString(BaseConfig.UserAccess))));

            rootNode.Add(new XElement(ns + "ExportToPDF", new XAttribute("Allow", BaseConfig.AllowExportToPDF)));

            if(!(string.IsNullOrWhiteSpace(BaseConfig.LocalWebserviceEndpoint))) {
                rootNode.Add(new XElement(ns + "WebService", new XAttribute("Endpoint", BaseConfig.LocalWebserviceEndpoint), new XAttribute("ChannelFormat", "%@_I_%d")));
            }

            if (BaseConfig.Logo != null) {
                XElement logoNode = new XElement(ns + "Logo", new XAttribute("ResourceID", BaseConfig.Logo.ID));
                rootNode.Add(logoNode);
            }

            if(BaseConfig.HomePage != null) {
                XElement homePageNode = new XElement(ns + "HomePage", new XAttribute("ResourceID", BaseConfig.HomePage.ID));
                rootNode.Add(homePageNode);
            }

            if(BaseConfig.EndPage != null) {
                XElement endPageNode = new XElement(ns + "EndPage", new XAttribute("ResourceID", BaseConfig.EndPage.ID));
                rootNode.Add(endPageNode);
            }

            XElement resourcesNode = new XElement(ns + "Resources", new XAttribute("Count", (Resources.Count + MediaResources.Count)));
            foreach (Guid resourceId in Resources.Keys) {
                GraphicResource resource = Resources[resourceId];
                XElement resourceNode = new XElement(ns + "Resource", new XAttribute("ID", resourceId),
                                                        new XAttribute("Size", resource.GraphicBytes.Length),
                                                        new XAttribute("Type", resource.ImageType));
                resourcesNode.Add(resourceNode);
            }
            foreach(Guid resourceId in MediaResources.Keys) {
                VideoResource resource = MediaResources[resourceId];
                XElement resourceNode = new XElement(ns + "Resource", new XAttribute("ID", resourceId),
                                                        new XAttribute("Size", resource.Length),
                                                        new XAttribute("Type", resource.MediaType));
                resourcesNode.Add(resourceNode);
            }
            rootNode.Add(resourcesNode);

            //Generate slide nodes in presentation order
            
            foreach(int id in _orderedSlideIds) {
                XElement slideConfig = new XElement(ns + "SlideConfig");
                slideConfig.Add(new XAttribute("SlideID", id));

                foreach(XElement pageNode in SlideNodes[id]) {
                    slideConfig.Add(pageNode);
                }
                rootNode.Add(slideConfig);
            }

            return rootNode;
        }

    }
}
